home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 22
/
AACD 22.iso
/
AACD
/
Resources
/
General
/
ProNET
/
src
/
drivers
/
mfc-parallel.s
< prev
next >
Wrap
Text File
|
1996-11-30
|
19KB
|
721 lines
*
* mfc-parallel (ProNET driver)
*
******* pronet-driver/--Overview-- ******************************************
*
* This file covers information on how to write custom ProNET drivers.
* As is usual with my distributions ;-(, the API has changed again, but if
* there was really a single custom driver out, it can be changed really easy.
*
* A ProNET driver is an ordinary executable residing in the DEVS:ProNET/
* directory. This executable will be loaded for every ProNET Unit requiring
* it; it doesn't have to be reentrant. The code is entered at the very
* first position, which must contain the Init routine.
*
* >> CHANGES FROM V2 DRIVERS <<
*
* New in V3: Exit-Routine, Init-Flags = 1
* New in v37: ReadFlush-Routine, Init-Flags (now called Version) = 2,
* Write only one chunk, Write timeout support (three return codes now).
*
*****************************************************************************
*
*
;**** The MFCDevKit is not very extensive in explaining how the PIA
;**** chips work, so I had to search for the stuff myself. Motorola's
;**** Internet site doesn't know any of their old chips.
;**** The following text was found in the Internet in an article about
;**** the Dragon/Tandy hardware. I found `m6821.html' via Altavista and
;**** the keywords 'mc 6821 pia' or similar.
;
; The Dragon and Tandy microcomputers have two Motorola 6821 peripheral
; interface adapters (PIA) mapped into the address space between $FF00 and
; $FF3F. The first of these, PIA-0, is located $FF00-$FF03, and the second,
; PIA-1, is located $FF20-$FF23. The Dragon 64 also has a third PIA at
; $FF04-$FF07 but this is not discussed here.
;
; Each PIA contains two 8-bit I/O ports, port A and port B, where each port
; and each of the 16 I/O pins are independently programmable as either input
; or output pins. Two control/interrupt lines are also associated with each
; port, allowing one extra input/output pin and one input-only pin per port.
; These pins can trigger an interrupt upon 0->1 or 1->0 transitions, or
; 'follow' outputs, and are therefore slightly more sophisticated than simple
; buffers, but can nevertheless be used simply as additional I/O.
;
; Each port is accessed and controlled through 3 registers. These are the
; peripheral data register (PDR), the data direction register (DDR) and the
; control register (CR). For PIA-0 port A the control register is mapped into
; the CPU address space at $FF01. However, only two memory addresses are used
; for each port and therefore both the PDR and DDR cannot be made available
; simultaneously. Instead bit 2 of the control register must be set or cleared
; to select whether it is the PDR or DDR which is visible at $FF00. This
; applies similarly to port B on PIA-0, and both ports of PIA-1.
;
; In normal operation the PDR is left visible for each port, with the DDR
; only made visible when necessary during configuration or reconfiguration.
;
; The format of the port registers is as follows:
;
; PDR - 8 bits of data as currently being input/output on the device pins.
; DDR - 8 bits, each indicating whether the corresponding PDR bits are for
; input (0) or output (1).
; CR - bit 7: control line 1 interrupt status (0 = idle, 1 = request) RO
; bit 6: control line 2 interrupt status (0 = idle, 1 = request) RO
; bit 5: control line 2 direction (0 = input, 1 = output) RW
; bit 4: control line 2 mode (0 = 1->0, 1 = 0->1 or 'follow') RW
; bit 3: control line 2 enable (0 = disable, 1 = enable) RW
; bit 2: PDR/DDR select (0 = DDR, 1 = PDR) RW
; bit 1: control line 1 mode (0 = 1->0, 1 = 0->1) RW
; bit 0: control line 1 enable (0 = disable, 1 = enable) RW
;
; RO = Read only
; RW = Read/write
*
* On the Amiga, all PIA registers are ZERO by default (after power-on).
*
; Format of one packet is:
; 2bytes LENGTH of data including next two words in WORDS-1 !!!!!
; 2bytes Destination port
; 2bytes Source port
; xbytes DATA
* I know this is HW banging, but at the moment I don't know how to do
* it in a different way.
DISABLEINTS macro
move.w #$4000,$dff09a
endm
ENABLEINTS macro
move.w #$c000,$dff09a
endm
include "A:OSmacros.i"
include "exec/exec.i"
include "hardware/mfcres.i"
include "hardware/piabits.i"
include "hardware/intbits.i"
include "P:include/devices/pronet.i"
include "exec_lib.i"
include "intuition_lib.i"
include "dos_lib.i"
******* pronet-driver/Init **************************************************
*
* NAME
* Init -- Initialize a ProNET driver.
*
* SYNOPSIS
* error = Init(drvdata, confstr, ID, version);
* D0 A0 A1 D0 D1
*
* STRPTR Init(struct PNDrvData*, STRPTR, ULONG, ULONG);
*
* FUNCTION
* This function is called whenever pronet.device launches a new Unit
* process.
*
* INPUTS
* drvdata - pointer to struct PNDrvData which must be filled out by
* this routine.
* confstr - pointer to the configuration string following the driver
* ID.
* ID - D0 contains the bytes "RST!" (not a pointer!). Check this to
* prevent unwanted effects when your driver is started by
* accident.
* version - This number identifies the version of pronet.device that is
* calling this driver. 0=V2, 1=V3, 2=v37; 0 and 1
* should be rejected with error code NULL!.
*
* RESULT
* error - NULL if everything went ok, or a pointer to an error string,
* of which ProNET will make a copy and hand it to the calling
* application. You could also return one of the magic cookies
* as defined in devices/pronet.h for standard errors.
*
* SEE ALSO
* devices/pronet.h
*
*****************************************************************************
*
*
Init movem.l d2-d7/a2-a6,-(sp)
move.l a0,a4
move.l a1,a5
cmp.l #"RST!",d0 ;compare ID
bne .ende
cmp.l #2,d1 ;0 was the error condition
bne .ende ;in ProNET V2/V3
move.l #PNDRVERR_WRONG_ARGS,RC
move.l a5,a0
bsr dec2slong
tst.w d4
bmi.s .ende
move.w d1,PORT
bsr dec2slong
tst.w d4
bmi.s .ende
move.w d1,MACHINE
bsr dec2slong
tst.w d4
bmi.s .ende
move.w d1,PRIORITY
bsr AllocPPort
tst.w d0
bne.s .ende
bsr InitTransfer
move.l 4.w,a6
move.l ThisTask(a6),a1
move.w PRIORITY(pc),d0
LIBCALL SetTaskPri
move.b INTsigbit(pc),pndd_ReadSignalBit(a4)
move.l #ReadQuery,pndd_ReadQuery(a4)
move.l #ReadFlush,pndd_ReadFlush(a4)
move.l #Read,pndd_Read(a4)
move.l #Write,pndd_Write(a4)
move.l #Exit,pndd_Exit(a4)
clr.l RC
.ende movem.l (sp)+,d2-d7/a2-a6
move.l RC(pc),d0
rts
RC dc.l 0
dc.b "$VER: mfc-parallel 37.0 (30.11.96)",13,10,0
even
PORT dc.w 0
MACHINE dc.w 0
PRIORITY dc.w 0
dec2slong ; konvertiert Dezimalstring ab (a0) zu Longword in D1 !!
; SIGNED!
moveq #-1,d4
moveq #0,d1
moveq #0,d3
move.b (a0),d0
cmp.b #"-",d0
bne.s .loop
addq.l #1,a0
st d3
.loop moveq #0,d0
move.b (a0)+,d0
sub.b #"0",d0
cmp.b #9,d0
bhi.s .oki
clr.l d4
move.l d1,d2
lsl.l #3,d1
add.l d2,d1
add.l d2,d1
add.l d0,d1
bra.s .loop
.oki tst.w d3
beq.s .ende
neg.l d1
.ende rts
******* pronet-driver/Exit **************************************************
*
* NAME
* Exit -- Terminate a ProNET driver.
*
* SYNOPSIS
* Exit();
*
* void Exit(void);
*
* FUNCTION
* This function is called whenever pronet.device launches a new Unit
* process. You must free all resources you allocated previously and
* return after that.
*
*****************************************************************************
*
*
Exit movem.l d2-d7/a2-a6,-(sp)
bsr ExitTransfer
bsr FreePPort
movem.l (sp)+,d2-d7/a2-a6
rts
******* pronet-driver/ReadQuery *********************************************
*
* NAME
* ReadQuery -- find out about an incoming packet.
*
* SYNOPSIS
* length, destport, srcport = ReadQuery();
* D0 D1 D2
*
* UWORD, UWORD, UWORD = ReadQuery(void);
*
* FUNCTION
* This function is called whenever the Unit's task was woken up by
* the signal bit you provided in drvdata->ReadSignalBit in the Init
* routine. The signal bit can either be the signal bit of an
* IORequest reply port or one triggered from an interrupt or
* process set up by you.
*
* This function call is used to find out some basic info
* about the incoming packet before reading the actual data.
*
* RESULT
* length - number of bytes of the actual packet data (see NOTES)
* destport - destination ProNET Port on this machine
* srcport - source Port on the remote machine
*
* NOTES
* If you find out that the ReadSignalBit was triggered by mistake,
* you can return 0 in the length word, in which case ProNET will go
* to sleep again. Otherwise it is *guaranteed* that either ReadFlush
* or Read will be called lateron, no other driver routines will be
* called in between.
*
*****************************************************************************
*
*
ReadQuery movem.l d3-d7/a2-a6,-(sp)
move.l PIAaddr(pc),a3
btst #PIAPAB_BUSY,PIA_PRA(a3)
bne.s .error ; check if packet is pending
bclr #PIACRB_DDR,PIA_CRA(a3)
bset #PIAPAB_PAPEROUT,PIA_PRA(a3) ;set POUT(ack) line to output
bset #PIACRB_DDR,PIA_CRA(a3)
lea buffer-2(pc),a4 ; first word is `empty'
moveq #3,d4 ; (due to handshake init)
bsr ReceiveData
movem.w buffer(pc),d0-d2
subq.w #2-1,d0
add.w d0,d0
movem.l (sp)+,d3-d7/a2-a6
rts
.error moveq #0,d0
movem.l (sp)+,d3-d7/a2-a6
rts
******* pronet-driver/ReadFlush *********************************************
*
* NAME
* ReadFlush -- Forget the incoming packet.
*
* SYNOPSIS
* ReadFlush();
*
* void ReadFlush(void);
*
* FUNCTION
* This function will only be called if a previous ReadQuery succeeded.
* ProNET wants us to forget the complete packet.
*
*****************************************************************************
*
*
ReadFlush movem.l d2-d7/a2-a6,-(sp)
move.w buffer(pc),d4
subq.w #2,d4
bsr FlushData
bra.s read_entry
******* pronet-driver/Read **************************************************
*
* NAME
* Read -- read the incoming packet data to a specified memory location.
*
* SYNOPSIS
* Read(memptr);
* A0
*
* void Read(APTR);
*
* FUNCTION
* This function will only be called if a previous ReadQuery succeeded.
* Here we will copy the packet's data to the specified memory location.
*
* INPUTS
* memptr - memory location to which we shall copy
*
*****************************************************************************
*
*
Read movem.l d2-d7/a2-a6,-(sp)
move.l a0,a4
move.w buffer(pc),d4
subq.w #2,d4
bsr ReceiveData
read_entry move.l PIAaddr(pc),a3
bclr #PIACRB_DDR,PIA_CRA(a3)
bclr #PIAPAB_PAPEROUT,PIA_PRA(a3) ;set POUT(ack) line to input
bset #PIACRB_DDR,PIA_CRA(a3)
movem.l (sp)+,d2-d7/a2-a6
rts
******* pronet-driver/Write *************************************************
*
* NAME
* Write -- Send a data packet to the remote machine.
*
* SYNOPSIS
* error = Write(dataptr, length, destport, srcport);
* D0 A0 D0 D1 D2
*
* ULONG Write(APTR, ULONG, UWORD, UWORD);
*
* FUNCTION
* I guess this will always be the most complex function of a driver.
* Try to get a connection to the remote machine and send the packet
* as requested. A kind of flow control, like a timeout feature,
* should be implemented.
*
* INPUTS
* dataptr - pointer to the data to be transmitted
* length - length of data to be transmitted. The length is limited
* to 16384 bytes and it is even.
* destport - destination Port on the remote machine
* srcport - source Port on this machine
*
* RESULT
* error - 0 if packet could be sent without trouble.
* -1 if the line is currently busy -- pronet.device will then
* try again later.
* 1 if the remote machine does not respond. The application
* will be notified in this case, CMD_WRITE returns with
* an error (PNDERR_DESTINATION_GONE).
*
* NOTES
* Your driver is responsible for correct transmission of the data,
* by using checksums or similar mechanisms.
*
*****************************************************************************
*
*
Write movem.l d2-d7/a2-a6,-(sp)
lea buffer(pc),a5
move.l a0,6(a5)
move.l d0,10(a5)
move.w d1,(a5)
move.w d2,2(a5)
move.w d0,d4
lsr.w #1,d4
addq.w #2-1,d4
bsr AcquireLine
tst.w d0 ;Line is busy now! Try later
bne.s .ende
move.l a5,a4
moveq #1,d4
moveq #0,d6
bsr SendData
move.l 6(a5),a4 ; The last word gets sent separately.
move.l 10(a5),d4
lsr.w #1,d4
subq.w #2,d4
bmi.s .0
bsr SendData
.0 DISABLEINTS * We don't take too long here
moveq #0,d4 * in case the other machine wants
bsr SendData * to send something...
clr.w sending
bsr InitParallel
ENABLEINTS
moveq #0,d0
.ende movem.l (sp)+,d2-d7/a2-a6
rts
dc.w 0
buffer dc.w 0,0,0
dc.l 0,0
; -- Allocate the specified MFC port.
AllocPPort move.l 4.w,a6
lea mfcname(pc),a1
LIBCALL OpenResource
move.l #err1,RC
move.l d0,mfcbase
beq.s APP_nores
move.l d0,a6
moveq #0,d0
move.w PORT(pc),d0
lea drivername(pc),a0
LIBCALL AllocPort
move.l #err2,RC
move.l d0,portnode
beq.s APP_noport
move.l d0,a0
move.l port_ChipNode(a0),a0
move.l chip_Product(a0),d0
move.l #err3,RC
cmp.l #CHIP_PIA,d0
bne.s APP_wrongcard
move.l chip_Address(a0),PIAaddr
moveq #0,d0
rts
FreePPort
APP_wrongcard move.l mfcbase(pc),a6
move.l portnode(pc),a0
LIBCALL FreePort
APP_noport
APP_nores moveq #-1,d0
rts
mfcname dc.b "mfc.resource",0
drivername dc.b "ProNET MultiFaceCard3 Driver",0
err1 dc.b "Can't find mfc.resource.",0
err2 dc.b "Can't open specified MFC parallel port.",0
err3 dc.b "The specified port is no MC6821 PIA.",0
even
mfcbase dc.l 0
portnode dc.l 0
PIAaddr dc.l 0
; -- Init Transfer routines
InitTransfer move.l 4.w,a6
move.l ThisTask(a6),INTsigtask
moveq #-1,d0
LIBCALL AllocSignal
move.b d0,INTsigbit
move.l 4.w,a6
moveq #INTB_PORTS,d0
lea INTstruct(pc),a1
LIBCALL AddIntServer
bsr InitParallel
rts
ExitTransfer
bsr ExitParallel
move.l 4.w,a6
moveq #INTB_PORTS,d0
lea INTstruct(pc),a1
LIBCALL RemIntServer
move.b INTsigbit(pc),d0
LIBCALL FreeSignal
rts
INTsigtask dc.l 0
INTsigbit dc.w 0
INTstruct dc.l 0,0
dc.b 2,0
dc.l drivername
dc.l 0,INTcode
INTcode moveq #0,d0
move.l PIAaddr(pc),a0
btst #PIACRB_IRQ1,PIA_CRB(a0) ;PIA IRQ1?
beq.s .no
move.b PIA_PRB(a0),d1 ;Clear IRQ bit
tst.w sending
bne.s .no
btst #PIAPAB_BUSY,PIA_PRA(a0) ;check if we
bne.s .no ;were really called
move.l 4.w,a6 ;or other machine
move.l INTsigtask(pc),a1 ;was switched on!
move.b INTsigbit(pc),d1
moveq #0,d0
bset d1,d0
LIBCALL Signal
moveq #1,d0
.no rts
;:---------------------------------------------------------------------------
;:-- InitParallel
InitParallel
move.l PIAaddr(pc),a0
move.b #%00000100,PIA_CRA(a0)
move.b #%00000111,PIA_PRA(a0) ;control lines to 1 (when we set them to output)
move.b #%00000000,PIA_CRA(a0)
move.b #%00000000,PIA_PRA(a0) ;control lines -> input
move.b #%00000100,PIA_CRA(a0) ;PR-Register is default
move.b #%00000000,PIA_CRB(a0)
sf PIA_PRB(a0) ;data lines input
move.b #%00000101,PIA_CRB(a0) ;CB1-interrupt enablen
rts
ExitParallel
move.l PIAaddr(pc),a0
move.b #%00000100,PIA_CRB(a0) ;C1-interrupt disablen
rts
;:---------------------------------------------------------------------------
;:-- AcquireLine
;:-- d4 Number of words-1 that will be sent !
;:-- !!!!!! RETURNS d0=0 if no error
;:-- d0=-1 if line was busy
AcquireLine
move.l PIAaddr(pc),a3
DISABLEINTS
btst #PIAPAB_BUSY,PIA_PRA(a3)
bne.s .free
ENABLEINTS
moveq #-1,d0
rts
.free st sending
* Set BUSY line
bclr #PIACRB_DDR,PIA_CRA(a3)
bset #PIAPAB_BUSY,PIA_PRA(a3) ;BUSY = output
bset #PIACRB_DDR,PIA_CRA(a3)
bclr #PIAPAB_BUSY,PIA_PRA(a3) ;BUSY = 0
* Toggle SELECT line to cause interrupt on server
bclr #PIACRB_DDR,PIA_CRA(a3)
bset #PIAPAB_SELECTED,PIA_PRA(a3) ;output
bset #PIACRB_DDR,PIA_CRA(a3)
bclr #PIAPAB_SELECTED,PIA_PRA(a3) ;toggle
bset #PIAPAB_SELECTED,PIA_PRA(a3) ;
bclr #PIACRB_DDR,PIA_CRA(a3)
bclr #PIAPAB_SELECTED,PIA_PRA(a3) ;input
bset #PIACRB_DDR,PIA_CRA(a3)
ENABLEINTS
tst.w firstpacketflag ; no timeout for the first packet!
beq.s .acknotimeout
tst.w MACHINE ; no timeout for machine 1!
bne.s .acknotimeout
move.l #100000,d0
.ack0 btst #PIAPAB_PAPEROUT,PIA_PRA(a3) ; wait for it to respond...
beq.s .ack0ok
subq.l #1,d0
bne.s .ack0
bset #PIAPAB_BUSY,PIA_PRA(a3)
bsr InitParallel ;Timeout! Reset all lines!
bsr INTcode
moveq #-1,d0
rts
.acknotimeout btst #PIAPAB_PAPEROUT,PIA_PRA(a3)
bne.s .acknotimeout
.ack0ok st firstpacketflag
bset #PIAPAB_BUSY,PIA_PRA(a3) ; and initialize handshake sequence
.ack1 btst #PIAPAB_PAPEROUT,PIA_PRA(a3)
beq.s .ack1
bclr #PIACRB_DDR,PIA_CRB(a3)
st PIA_PRB(a3) ; DATA = output
bset #PIACRB_DDR,PIA_CRB(a3)
ror.w #8,d4 ; and hand the length word over
move.b d4,PIA_PRB(a3) ; to the remote machine
bclr #PIAPAB_BUSY,PIA_PRA(a3)
ror.w #8,d4
.ack2 btst #PIAPAB_PAPEROUT,PIA_PRA(a3)
bne.s .ack2
move.b d4,PIA_PRB(a3)
bset #PIAPAB_BUSY,PIA_PRA(a3)
.ack3 btst #PIAPAB_PAPEROUT,PIA_PRA(a3)
beq.s .ack3
moveq #0,d0
rts
firstpacketflag dc.w 0
sending dc.w 0
;:---------------------------------------------------------------------------
;:-- SendData
;:-- a4 *data
;:-- d4 datalength in words-1
SendData
move.l PIAaddr(pc),a2
lea PIA_PRB(a2),a0
lea PIA_PRA(a2),a2
move.b #%11111110,d0 ; clear BUSY
move.b #%00000001,d1 ; set BUSY
.loop move.b (a4)+,(a0) ; fully handshaked data txfer
and.b d0,(a2)
.ack0 btst #PIAPAB_PAPEROUT,(a2)
bne.s .ack0
move.b (a4)+,(a0)
or.b d1,(a2)
.ack1 btst #PIAPAB_PAPEROUT,(a2)
beq.s .ack1
dbra d4,.loop
rts
;:---------------------------------------------------------------------------
;:-- ReceiveData
;:-- a4 *datadest
;:-- d4 datalength in words-1
ReceiveData
move.l PIAaddr(pc),a2
lea PIA_PRB(a2),a0
lea PIA_PRA(a2),a2
move.b #%11111101,d0 ; clear POUT
move.b #%00000010,d1 ; set POUT
.loop btst #PIAPAB_BUSY,(a2)
bne.s .loop
move.b (a0),(a4)+
and.b d0,(a2)
.ack0 btst #PIAPAB_BUSY,(a2)
beq.s .ack0
move.b (a0),(a4)+
or.b d1,(a2)
dbra d4,.loop
rts
FlushData
move.l PIAaddr(pc),a2
lea PIA_PRB(a2),a0
lea PIA_PRA(a2),a2
move.b #%11111101,d0 ; clear POUT
move.b #%00000010,d1 ; set POUT
.loop btst #PIAPAB_BUSY,(a2)
bne.s .loop
and.b d0,(a2)
.ack0 btst #PIAPAB_BUSY,(a2)
beq.s .ack0
or.b d1,(a2)
dbra d4,.loop
rts